










































import threading
import json
import flask
import logging
from collections import deque


class Website:
    __app_map = {}
    __lock = threading.Lock()

    def __init__(self, port):
        log = logging.getLogger('werkzeug')
        log.setLevel(logging.ERROR)

        port = int(port)
        with self.__lock:
            if port in self.__app_map:
                app = self.__app_map[port]
            else:
                app = flask.Flask(__name__)
                self.__app_map[port] = app
                threading.Thread(target=app.run, kwargs={'host': '0.0.0.0', 'port': port}, daemon=True).start()
            self.app = app

    def add_page(self, path):
        return Page(self.app, path)

    def add_api(self, path):
        return Api(self.app, path)

    def add_list(self, path, max_len=10000):
        return WebList(self.app, '', path, max_len)

    def add_deque(self, path, max_len=100000):
        return WebDeque(self.app, '', path, max_len)


class Page:
    def __init__(self, app, path=''):
        self.app = app
        self.path = path
        self.items = []
        self._form_name_to_fn = {}

        blue_print = flask.Blueprint(path, __name__)

        @blue_print.route(path)
        def route():
            args = flask.request.args
            if 'function' in args:
                return self._execute(args)
            return self.render()

        app.register_blueprint(blue_print)

    def add_content(self, header=None, render_fn=None):
        html: Html = Html(header, render_fn)
        self.items.append(html)
        return html

    def add_header(self, header):
        html = Html(header, lambda: '', False)
        self.items.append(html)

    def add_input(self, name, fn):
        assert name not in self._form_name_to_fn
        self._form_name_to_fn[name] = fn
        form = PageForm(self, name)
        self.items.append(form)
        return form

    def add_button_collection(self):
        form = ButtonCollection(self)
        self.items.append(form)
        return form

    def render(self):
        strings = [item.render() for item in self.items]
        result = ''.join(strings)
        return result

    def _execute(self, args):
        try:
            name = args['function']
            kwargs = {k: v for k, v in args.items()}
            del kwargs['function']
            fn = self._form_name_to_fn[name]
            return fn(**kwargs)
        except Exception as err:
            return str(err)


class PageForm:
    def __init__(self, page, name):
        self.page = page
        self.name = name
        self._inputs = []

    def render(self):
        inputs = []
        for name, hint in self._inputs:
            inputs .append("""<input type="text" placeholder="{}" name="{}">""".format(
                hint, name
            ))
        inputs = '\n'.join(inputs)

        content = """
            <form action="" method="get">
                <div>
                    <input type="hidden" name="function" value="{}">
                    {}
                    <button class="btn btn-primary">{}</button>
                </div>
            </form>
        """.format(self.name, inputs, self.name)
        return content

    def add_box(self, name, hint=''):
        self._inputs.append((name, hint))


class ButtonCollection:
    def __init__(self, page):
        self.page = page
        self.buttons = []  # [(name, action), ]

    def render(self):
        form = """
            <form action="{}" method="get">
                <td>
                    <button class="btn btn-primary">{}</button>
                </td>
            </form>
        """

        forms = [form.format(action, name) for name, action in self.buttons]
        forms = ' '.join(forms)

        content = '<table><tr>{}</tr></table>'.format(forms)
        return content

    def add_button(self, name, action):
        self.buttons.append((name, action))


class Api:
    def __init__(self, app, path=''):
        self.app: flask.Flask = app
        self.path = path

    def add_function(self, endpoint, func):
        full = '{}{}'.format(self.path, endpoint)
        blue_print = flask.Blueprint(full, __name__)

        @blue_print.route(full)
        def call():
            kwarg = flask.request.args.to_dict()
            try:
                result = func(**kwarg)
                return str(result)
            except Exception as err:
                return str(err)

        self.app.register_blueprint(blue_print)


class WebList:
    def __init__(self, app, root, path, max_len):
        self.max_len = max_len
        self._list = []

        blue_print = flask.Blueprint(path, __name__, url_prefix=root)
        @blue_print.route(path)
        def route():
            return json.dumps(self._list)

        app.register_blueprint(blue_print)

    def __len__(self):
        return len(self._list)

    def append(self, value):
        self._list.append(value)
        while len(self._list) > self.max_len:
            self._list.pop(0)

    def pop_left(self):
        self._list.pop(0)


class WebDeque:
    def __init__(self, app, root, path, max_len):
        self.max_len = max_len
        self._dq = deque(maxlen=max_len)

        blue_print = flask.Blueprint(path, __name__, url_prefix=root)
        @blue_print.route(path)
        def route():
            li = list(self._dq)
            j = json.dumps(li)
            return j

        app.register_blueprint(blue_print)

    def __len__(self):
        return len(self._dq)

    def __getitem__(self, item):
        return self._dq.__getitem__(item)

    def __repr__(self):
        return str(self._dq)

    def append(self, value):
        self._dq.append(value)

    def pop_left(self):
        self._dq.popleft()


class Html:
    def __init__(self, header, render_fn, br=True):
        if header:
            header = '<h3>{}</h3>'.format(header)
        else:
            header = ''

        self._render_fn = render_fn
        self._br = br
        self._header = header
        self._html = ''

    def render(self):
        if self._render_fn:
            html = self._render_fn()
        else:
            html = self._html
        result = self._header + html
        if self._br:
            result += '<br>'
        return result

    def set_html(self, html_str):
        self._html = html_str


if __name__ == '__main__':
    web = Website(3391)
    api = web.add_api('/api')

    def func(a):
        print('func({})'.format(a))
        return 'suceess'

    def func_b(x):
        print('func({})'.format(x))

    api.add_function('/func_a', func)

    input(':')




















